using System.Collections.Generic;
using System.Diagnostics;
using NUnit.Framework;
using TestCases.CompareAssert.Core;
using TestCases.CompareAssert.Core.Comparers;
using TestCases.CompareAssert.Core.Comparers.Interfaces;
using TestCases.CompareAssert.Core.Formatters;
using TestCases.CompareAssert.Core.Interfaces;
using TestCases.CompareAssert.Core.Builders;
using TestCases.Utils.Pairs;

namespace TestCases.Tests.Helpers
{
    public static class TestAssert
    {
        public static ICompareResult AssertResult(
            this ICompareResult result, 
            IObjectComparer comparer, 
            CompareStatus status, 
            IPair<IComparerObject, IComparerObject> pair)
        {
            Assert.That(result.Left, Is.SameAs(pair.Left));
            Assert.That(result.Right, Is.SameAs(pair.Right));
            Assert.That(result.Status, Is.EqualTo(status));
            Assert.That(result.Comparer, Is.EqualTo(comparer));
            return result;
        }

        public static IResultBuilder AssertFullBuilder(
            this IResultBuilder builder, 
            IObjectComparer comparer, 
            CompareStatus status, 
            IPair<IComparerObject, IComparerObject> pair, 
            IEnumerable<ICompareResult> childResults)
        {
            Assert.That(builder, Is.InstanceOf<ResultBuilder>());            
            var result = builder.GetResult();
            AssertResult(result, comparer, status, pair);                       
            if (childResults != null)
            {
                Assert.That(result.ChildResults, Is.EqualTo(childResults));
            }
            return builder;      
        }
        
        public static IResultBuilder AssertReadOnlyBuilder(
            this IResultBuilder builder, 
            ICompareResult result)
        {
            Assert.That(builder, Is.InstanceOf<ReadOnlyBuilder>());
            Assert.That(builder.GetResult(), Is.SameAs(result));
            return builder;
        }

        public static ICompareContext AssertContext(
            this ICompareContext context, 
            IResultBuilderFactory factory, 
            IComparerObject leftObj, 
            IComparerObject rightObj,
            bool testObj)
        {
            Assert.That(context.Factory, Is.SameAs(factory));
            if (testObj)
            {
                Assert.That(context.Left, Is.SameAs(leftObj));
                Assert.That(context.Right, Is.SameAs(rightObj));
            }
            Assert.That(context.LPath, Is.EqualTo(leftObj.Path));
            Assert.That(context.RPath, Is.EqualTo(rightObj.Path));
            Assert.That(context.LType, Is.EqualTo(leftObj.Object.GetType()));
            Assert.That(context.RType, Is.EqualTo(rightObj.Object.GetType()));
            Assert.That(context.LValue, Is.EqualTo(leftObj.Object));
            Assert.That(context.RValue, Is.EqualTo(rightObj.Object));
            return context;
        }

        public static IPair<TLeft, TRight> AssertPair<TLeft, TRight>(this IPair<TLeft, TRight> actual, IPair<TLeft, TRight> expected)
        {
            Assert.That(actual.Left, Is.EqualTo(expected.Left));
            Assert.That(actual.Right, Is.EqualTo(expected.Right));
            return actual;
        }

        public static IObjectExpression CallExpressionMethods(this IObjectExpression expr)
        {            
            expr.IsNot(TestData.Type1);
            expr.IsNot(TestData.Type1.ToString());
            expr.IsNot<int>();
            return expr;           
        }
        
        public static ICompareResult AssertTemplate(
            this IObjectComparer comparer, 
            object obj1, 
            object obj2, 
            IList<IPair<string, object>> expectedMessages)
        {
            return comparer.Compare(new TestBuilderFactory(comparer, expectedMessages), obj1, obj2);
        }
        
        public static ICompareResult AssertStatus(this ICompareResult result, CompareStatus status)
        {
            Assert.That(result.Status, Is.EqualTo(status), "Statuses are different. Result trace: \n" + result.FormatResult());
            return result;
        }
        
        public static string FormatResult(this ICompareResult result)
        {
            return ResultFormatter.Instance.FormatResult(result);
        }
        
        public static ICompareResult TraceResult(this ICompareResult result)
        {
            Trace.WriteLine(result.FormatResult());
            return result;
        }

        public static void AssertStatus(this IObjectComparer comparer, CompareStatus status)
        {
            Assert.That(comparer.Compare(TestData.CompareContext1).GetStatus(), Is.EqualTo(status));
        }
    }
}